# -*- encoding: utf-8 -*-
# @Author：lijinxi
# @Time ：2020/12/1 11:52
# @File：socket_server.py

import socket
import logging
import logging.config
import threading
import time
import os
from database import DataBase


class SocketServer:
    def __init__(self, addr='localhost', port=9999):
        self.addr = addr  #
        hostname = socket.gethostname()
        # 获取本机ip
        ip = socket.gethostbyname(hostname)
        self.addr = ip
        self.port = port
        self.current_dir = '/home/ftpuser'  # 默认目录

        logging.config.fileConfig('../config/logging.cfg')  # 配置日志

        # 数据库
        self.database = DataBase()

        # 主的监听进程
        self.cmd_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  # 命令
        self.upload_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  # 上传
        self.download_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  # 下载

        # 文件
        self.upload_file_path = ''  # 上传文件要保存的位置
        self.upload_file_summary = dict()

        self.download_file_path = ''  # 下载文件存在的位置
        self.download_file_content = []  # 存放分片

        self.upload_signal = False
        self.download_signal = False  # 线程开始信号,  可能会出错, 但作者不想改了

    def cmd_listen(self):  # 监听命令连接请求
        self.cmd_server.bind((self.addr, self.port))
        self.cmd_server.listen(10)  # max_thread
        print(f'cmd_server start at {self.addr}:{self.port}.')
        print('waiting for connection...')
        logging.info(f'cmd_server start at {self.addr}:{self.port}.')
        logging.info('waiting for connection...')

        while True:
            sock, addr = self.cmd_server.accept()
            cmd_thread = threading.Thread(target=self.cmd_tcp_link, args=(sock, addr), daemon=True)
            cmd_thread.start()

    def cmd_tcp_link(self, sock, addr):
        print('cmd, accept new connection from %s:%s...' % addr)
        logging.info('cmd, accept new connection from %s:%s...' % addr)

        # 登陆用户验证
        cmd_data = eval(sock.recv(1024).decode('utf-8'))
        print(f'received user login info: {cmd_data}.')
        logging.info(f'received user login info: {cmd_data}.')
        msg, status_code = self.database.query_user(cmd_data['username'], cmd_data['password'])
        print(f'authorized {status_code}, {msg}.')
        logging.info(f'authorized {status_code}, {msg}.')
        sock.send((msg + ':' + status_code).encode('utf-8'))
        if status_code != 'OK':
            sock.close()  # 验证失败
            return

        # 处理cmd指令
        while True:
            cmd_data = eval(sock.recv(1024).decode('utf-8'))
            key = list(cmd_data.keys())[0]

            if key == 'ls_remote_dir':  # 列出文件和文件夹
                abs_path, folders, files, sizes = self.handle_ls(cmd_data[key])

                print(f'ls_remote_dir:{abs_path}')
                logging.info(f'ls_remote_dir:{abs_path}')

                new_data = {
                    'abs_path': abs_path,
                    'folders': folders,
                    'files': files,
                    'sizes': sizes
                }
                sock.send(str(new_data).encode('utf-8'))

            if key == 'download':  # 下载
                self.download_file_content.clear()
                datalist, size = self.file_summary(cmd_data[key])  # download

                print(f'it will be download :{cmd_data[key]}')
                logging.info(f'it will be download :{cmd_data[key]}')

                self.download_file_content = datalist
                self.download_file_path = cmd_data[key]
                summary = {
                    'len': len(datalist),
                    'size': size,
                }
                sock.send(str(summary).encode('utf-8'))  # 发送summary
                self.download_signal = True

            if key == 'recv_summary_download':  # 摆设
                pass

            if key == 'upload':  # 上传
                remote_path = cmd_data[key]  # remote_path
                self.upload_file_path = remote_path
                summary = cmd_data['summary']
                self.upload_file_summary = summary

                print(f'it will be uploaded : {remote_path}')
                logging.info(f'it will be uploaded : {remote_path}')

                # cmd_data = {
                #  'recv_summary_upload': ''
                # }
                # sock.send(str(cmd_data).encode('utf-8'))  # 发送收到summary
                self.upload_signal = True

            time.sleep(1)
            if not cmd_data or key == 'exit':
                break
        sock.close()
        print('Connection from %s:%s closed.' % addr)

    def upload_listen(self):
        self.upload_server.bind((self.addr, self.port + 100))
        self.upload_server.listen(10)
        print(f'upload_server start at {self.addr}:{self.port + 100}.')
        print('waiting for connection...')

        while True:
            sock, addr = self.upload_server.accept()
            while self.upload_signal:
                upload_thread = threading.Thread(target=self.upload_tcp_link, args=(sock, addr), daemon=True)
                self.upload_signal = False
                upload_thread.start()

    def upload_tcp_link(self, sock, addr):
        print('upload, accept new connection from %s:%s...' % addr)
        logging.info('upload, accept new connection from %s:%s...' % addr)

        with open(self.upload_file_path, 'wb') as f:
            for i in range(self.upload_file_summary['len']):
                f.write(sock.recv(1024))
        sock.close()  # 关闭连接

    def download_listen(self):
        self.download_server.bind((self.addr, self.port + 200))
        self.download_server.listen(10)
        print(f'download_server start at {self.addr}:{self.port + 200}.')
        print('waiting for connection...')

        while True:
            sock, addr = self.download_server.accept()
            upload_thread = threading.Thread(target=self.download_tcp_link, args=(sock, addr), daemon=True)
            upload_thread.start()

    def download_tcp_link(self, sock, addr):
        print('download, accept new connection from %s:%s...' % addr)
        logging.info('download, accept new connection from %s:%s...' % addr)

        for data in self.download_file_content:
            sock.send(data)
        sock.close()

    @staticmethod
    def handle_ls(path):
        folders = []
        files = []
        sizes = []
        for i in os.listdir(path):
            if os.path.isdir(os.path.join(path, i)):
                folders.append(i)
            else:
                size = str(round(os.stat(os.path.join(path, i)).st_size / 1024))  # 文件大小
                files.append(i)
                sizes.append(size)
        return os.path.abspath(path), folders, files, sizes

    @staticmethod
    def file_summary(path):
        size = os.stat(path).st_size  # 文件大小
        datalist = []
        with open(path, 'rb') as f:
            while True:
                _data = f.read(1024)
                if not _data:
                    break
                datalist.append(_data)
        return datalist, size


if __name__ == '__main__':
    ss = SocketServer()
    _cmd_thread = threading.Thread(target=ss.cmd_listen, args=())
    _cmd_thread.start()
    _upload_thread = threading.Thread(target=ss.upload_listen, args=())
    _upload_thread.start()
    _download_thread = threading.Thread(target=ss.download_listen, args=())
    _download_thread.start()
